#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include "LecteurFonction.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

void NoeudSeqInst::genCode(ostream &f, unsigned short indentation) {
    for (unsigned int i = 0; i < tabInst.size(); i++) {
        tabInst[i]->genCode(f, indentation);
        if (typeid (*tabInst[i]) == typeid (NoeudInstEcrire) ||
                        typeid (*tabInst[i]) == typeid (NoeudAffectation) ||
                        typeid (*tabInst[i]) == typeid (NoeudInstLire) ||
                        typeid (*tabInst[i]) == typeid (NoeudFonction))
            f << ";\n";
    }
}

int NoeudSeqInst::getValeur() {
    int valeur = 0;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        // on evalue chaque instruction de la séquence
        valeur = tabInst[i]->getValeur();
    return valeur;
}


void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}


void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

void NoeudAffectation::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << ((SymboleValue*) variable)->getChaine() << " = ";
    expression->genCode(f, 0);
}
////////////////////////////////////////////////////////////////////////////////

int NoeudAffectation::getValeur() {
    int valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

void NoeudOperateurBinaire::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << '(';
    if (operateur == "non") {
        f << "!";
    } else {
        operandeGauche->genCode(f, 0);
        if (operateur.getChaine() == "et") {
            f << " && ";
        } else if (operateur.getChaine() == "ou") {
            f << " || ";
        } else {
            f << operateur.getChaine();
        }
    }
    operandeDroit->genCode(f, 0);
    f << ')';
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurBinaire::getValeur() {
    int valeur = 0;
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();
    if (this->operateur == "+") valeur = og + od;
    else if (this->operateur == "-") valeur = og - od;
    else if (this->operateur == "*") valeur = og * od;
    else if (this->operateur == "==")valeur = (og == od);
    else if (this->operateur == "!=")valeur = (og != od);
    else if (this->operateur == "et")valeur = (og && od);
    else if (this->operateur == "ou")valeur = (og || od);
    else if (this->operateur == "non")valeur = (!od);
    else if (this->operateur == "<")valeur = (og < od);
    else if (this->operateur == ">")valeur = (og > od);
    else if (this->operateur == "<=")valeur = (og <= od);
    else if (this->operateur == ">=")valeur = (og >= od);

    else // this->operateur=="/"
        if (od != 0)
        valeur = og / od;
    else {
        cout << "Erreur pendant l'interpretation : division par zero" << endl;
        exit(0); // plus tard on levera une exception
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}



////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

int NoeudInstSi::ajoute(Noeud* cond, Noeud* seq) {
    vectCond.push_back(cond);
    vectSeqInst.push_back(seq);
    return 0;
}
////////////////////////////////////////////////////////////////////////////////

int NoeudInstSi::getValeur() {
    unsigned i = 0;
    while (i < vectCond.size() && vectCond[i] != NULL && !vectCond[i]->getValeur()) {
        i++;
    }
    if (i < vectCond.size()) {
        vectSeqInst[i]->getValeur();
    }
    return 0;
}
////////////////////////////////////////////////////////////////////////////////

void NoeudInstSi::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << "if ";
    vectCond[0]->genCode(f, 0);
    f << "{\n";
    vectSeqInst[0]->genCode(f, indentation + 1);
    Noeud::genCode(f, indentation);
    f << "}\n";
    for (unsigned i = 1; i < vectCond.size(); i++) {
        Noeud::genCode(f, indentation);
        if (vectCond[i] != NULL) {
            f << "else if ";
            vectCond[i]->genCode(f, 0);
        } else {
            f << "else";
        }
        f << " {\n";
        vectSeqInst[i]->genCode(f, indentation + 1);
        Noeud::genCode(f, indentation);
        f << "}\n";
    }
}

void NoeudInstSi::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << vectCond.size() << " condition(s)" << endl;
    for (unsigned int i = 0; i < vectCond.size(); i++) {
        if (vectCond[i] != NULL)
            vectCond[i]->afficher(indentation);
        else {
            Noeud::afficher(indentation);
            cout << "sinon\n";
        }
        vectSeqInst[i]->afficher(indentation + 1);
    }
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

int NoeudInstLire::getValeur() {
    int valeur;
    cin >> valeur;
    std::cin.clear();
    cin.ignore(256, '\n');
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

void NoeudInstLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Lire" << endl;
    variable->afficher(indentation + 1); // on affiche la variable
}

void NoeudInstLire::ajoute(Noeud*var) {
    variable = var;
}

void NoeudInstLire::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << "cin >> ";
    variable->genCode(f, 0);
    f << ";\n";
    Noeud::genCode(f, indentation);
    f << "cin.clear(); cin.ignore(256, '\\n')";
}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

int NoeudInstEcrire::getValeur() {
    if (typeid (*ecrit) == typeid (SymboleValue) && *((SymboleValue*) ecrit) == "<CHAINE>") {
        string s = ((SymboleValue*) ecrit)->getChaine();
        cout << s.substr(1, s.length() - 2);
    } else
        cout << ecrit->getValeur();
    return 0;
}

void NoeudInstEcrire::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << "cout << ";
    ecrit->genCode(f, 0);
}

void NoeudInstEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Ecrire" << endl;
    ecrit->afficher(indentation + 1);
}

void NoeudInstEcrire::ajoute(Noeud*var) {
    ecrit = var;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

int NoeudInstPour::getValeur() {
    _affect1->getValeur();
    while (_exp->getValeur()) {
        _seq->getValeur();
        _affect2->getValeur();
    }
    return 0;
}

void NoeudInstPour::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << "for (";
    _affect1->genCode(f, 0);
    f << "; ";
    _exp->genCode(f, 0);
    f << "; ";
    _affect2->genCode(f, 0);
    f << ") {\n";
    _seq->genCode(f, indentation + 1);
    Noeud::genCode(f, indentation);
    f << "}\n";
}

void NoeudInstPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Pour (affectation d'entrée, test, affectation bouclée, instructions)\n";
    _affect1->afficher(indentation + 1);
    _exp->afficher(indentation + 1);
    _affect2->afficher(indentation + 1);
    _seq->afficher(indentation + 1);
}

void NoeudInstPour::ajoute(Noeud* affect1, Noeud* exp, Noeud* affect2, Noeud* seq) {
    _affect1 = affect1;
    _exp = exp;
    _affect2 = affect2;
    _seq = seq;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

int NoeudInstTq::getValeur() {
    while (_exp->getValeur()) {
        _seq->getValeur();
    }
    return 0;
}

void NoeudInstTq::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Tant que (condition d'entrée, instructions)\n";
    _exp->afficher(indentation + 1);
    _seq->afficher(indentation + 1);
}

void NoeudInstTq::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << "while (";
    _exp->genCode(f, 0);
    f << ") {\n";
    _seq->genCode(f, indentation + 1);
    Noeud::genCode(f, indentation);
    f << "}\n";
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
///////////////////////////////////////////////////////////////////////////////

int NoeudInstRepeter::getValeur() {
    do _seq->getValeur(); while (!_exp->getValeur());
    return 0;
}

void NoeudInstRepeter::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << "do {\n";
    _seq->genCode(f, indentation + 1);
    Noeud::genCode(f, indentation);
    f << "} while (!(";
    _exp->genCode(f, 0);
    f << "));\n";
}

void NoeudInstRepeter::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Repeter (condition de sortie, instructions)\n";
    _exp->afficher(indentation + 1);
    _seq->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudRetour
///////////////////////////////////////////////////////////////////////////////


void NoeudRetour::afficher(unsigned short indentation){
    Noeud::afficher(indentation);
    cout << "Noeud - Retour de fonction :\n";
    expression->afficher(indentation+1);
}

void NoeudRetour::genCode(ostream &f, unsigned short indentation) {
    Noeud::genCode(f, indentation);
    f << "return ";
    expression->genCode(f, 0);
    f << ";\n";
}

////////////////////////////////////////////////////////////////////////////////
// NoeudFonction
///////////////////////////////////////////////////////////////////////////////


int NoeudFonction::getValeur(){
    vector<int> v;
    LecteurFonction *lef = (LecteurFonction *)lf;
    for (int i=0; i<lef->getNbParam(); i++)
        v.push_back(vectParam[i]->getValeur());
    return lef->getValeur(v);
}

void NoeudFonction::afficher(unsigned short indentation){
    Noeud::afficher(indentation);
    LecteurFonction *lef = (LecteurFonction *)lf;
    cout << "Noeud - Appel de la fonction " << lef->getNom() << "() :\n";
    Noeud::afficher(indentation);
    cout << "Nombre de paramètres : " << lef->getNbParam() << " : \n";
    for (int i=0; i<lef->getNbParam(); i++)
        vectParam[i]->afficher(indentation+1);
}

void NoeudFonction::genCode(ostream &f, unsigned short indentation){
    Noeud::genCode(f, indentation);
    LecteurFonction *lef = (LecteurFonction *)lf;
    f << lef->getNom() << "(";
    for (int i=0; i<lef->getNbParam(); i++){
        vectParam[i]->genCode(f, 0);
        if (i<lef->getNbParam()-1)
            f << ", ";
    }
    f << ')';
}